Β - Last update: 2023-10-29

ν•΄μ‹œ κ΄€λ ¨ 정리

Collision control

hashμ—μ„œ 좩돌 방지 정책은 μ€‘μš”ν•˜λ‹€. κ°€μž₯ κ°„λ‹¨ν•˜κ²ŒλŠ” 이미 μ‚¬μš©μ€‘μΈ μ˜μ—­μ„ λ§Œλ‚¬μ„ λ•Œ μ£Όμ†Œκ°’μ„ +1 ν•˜λŠ” Open Addressing의 Linear Probing 방식이 λŒ€ν‘œμ μ΄μ§€λ§Œ, μ—¬κΈ°μ„œλŠ” Chaining λ°©μ‹μ˜ κ΅¬ν˜„μ„ μ•Œμ•„λ³΄κ² λ‹€. Chaining λ°©μ‹μ˜ 경우 κ°’ μ‚­μ œλ„ μ›ν• ν•˜κ²Œ ν•  수 μžˆμ–΄ μ’€ 더 μΆ”μ²œλœλ‹€. (Open Addressing은 μ‚­μ œκ°€ μ’€ κΉŒλ‹€λ‘­λ‹€. κ·Έλƒ₯ isDeleted λ„μž…ν•˜λŠ”κ±° λ§κ³ λŠ”..)

Integer Hash Key

idκ°€ sparseν•˜κ²Œ 쓰일 λ•Œ μ‚¬μš©. 본격적인 hash κ΅¬ν˜„ 이전에 ν•œλ²ˆ κ΅¬ν˜„ν•΄λ³΄λ©΄ μ’‹λ‹€. 사싀 hash의 관건은 keyκ°’ 생성에 μžˆλŠ”λ°, 이 경우 λ‹¨μˆœνžˆ val % HMAX둜 key 값을 작으면 μΆ©λΆ„ν•˜λ‹€. λ¬Όλ‘  μ•…μ˜μ μœΌλ‘œ valueλ₯Ό 생성할 μˆ˜λ„ 있긴 ν•˜λ‹€. 이λ₯Ό 막기 μœ„ν•΄μ„œλŠ” bucket을 ν™œμš©ν•΄μ•Ό ν•  것이닀. bucket을 ν™œμš©ν•œ 일반적인 κ΅¬ν˜„μ€ μ•„λž˜μ²˜λŸΌ λœλ‹€.

#define HMAX 10007 // μ†Œμˆ˜κ°€ μ’‹λ‹€.
struct Node {
int idx;
T val;
Node* next;
};
Node npool[1'000'000];
int nidx;
Node* newNode() {
npool[nidx].next = nullptr;
return &npool[nidx++];
}
struct Hash {
Node* head[HMAX];
Hash() {
for(int i = 0; i < HMAX; ++i) {
head[i] = newNode();
}
}
int getKey(int idx) {
return idx % HMAX;
}
void set(int idx, T val) {
Node* p = head[getKey(idx)];
for(;p->next != 0; p = p->next) {
if (p->next->idx == idx) {
p->next->val = val;
return;
}
}
p->next = newNode();
p->next->idx = idx;
p->next->val = val;
}
void insert(int idx, T val) {
// 쀑볡값이 ν™•μ‹€νžˆ μ—†μŒμ„ μ•Œ λ•Œ
Node* p = head[getKey(idx)];
Node* opnext = p->next;
p->next = newNode();
p->next->idx = idx;
p->next->val = val;
p->next->next = opnext;
}
T get(int idx) {
Node* p = head[getKey(idx)];
for(;p->next != 0; p = p->next) {
if (p->next->idx == idx) {
return p->next->val;
}
}
}
};

String Hash Key

주둜 hash라고 λ§ν•˜λ©΄, 보톡 string을 key둜 κ°–λŠ” hashλ₯Ό μ˜λ―Έν•˜λŠ” κ²½μš°κ°€ λ§Žλ‹€. 이 경우, 사싀 key값을 λ§Œλ“œλŠ” λ‘œμ§μ„ μ œμ™Έν•˜λ©΄ λ‘œμ§μ€ μœ„μ™€ κ°™λ‹€. 이런 key값을 효율적으둜, λΉ λ₯΄κ²Œ λ§Œλ“œλŠ” 것이 μ£Όμš”ν•œλ°, λ§Œμ•½ string의 μ΅œλŒ€ 길이가 8보닀 μž‘λ‹€κ³  ν•˜λ©΄, μ•„λž˜ 방법도 μœ νš¨ν•˜λ‹€. (κ°•λ ₯ μΆ”μ²œ)

int getKey(char d[]) {
char buf[8] = {0, };
for(int i = 0; d[i] != 0 && i < 8; ++i) {
buf[i] = d[i];
}
return (*(unsigned long long *) buf) % HMAX;
}

μœ„ 방식은 μΌμ’…μ˜ 트릭으둜, char 배열을 ull둜 μ†μ—¬μ„œ κ³„μ‚°ν•œλ‹€. λ§Œμ•½ d[]κ°€ 무쑰건 8자리둜만 μ˜¨λ‹€λ©΄, 볡사 κ³Όμ • ν•„μš”μ—†μ΄ λ°”λ‘œ μ‚¬μš©ν•΄λ„ λœλ‹€.

일반적으둜 κΈ΄ λ¬Έμžμ—΄μ΄ key 값에 쓰인닀면, λΉ„νŠΈμ—°μ‚°μ„ 적절히 ν™œμš©ν•΄μ„œ 각 자리 λ§ˆλ‹€ *33 μ •λ„μ˜ 값을 κ³±ν•΄μ„œ λ”ν•΄μ„œ μ“°κ³€ ν•œλ‹€. 이 경우 *33은 (h<<5 + h)둜 μΉ˜ν™˜μ΄ κ°€λŠ₯ν•˜λ―€λ‘œ, μ•„λž˜μ²˜λŸΌ μ“Έ 수 μžˆλ‹€.

int getKey(char d[]) {
int hashKey = 5381;
for(char* p = d; *p != 0; ++p) {
hashKey = ((hashKey) << 5) + hashKey + (*p);
hashKey %= HMAX;
}
return hashKey;
}

처음 hashKeyλ₯Ό μ΄ˆκΈ°ν™” ν•˜λŠ” 것을 μžŠμ§€ 말자.

Simple Hash structure

vectorλ₯Ό ν™œμš©ν•˜λ©΄ μœ„λ³΄λ‹€ 훨씬 μ‰½κ²Œ κ΅¬ν˜„λ„ κ°€λŠ₯ν•˜λ‹€. (μΆ”μ²œ)

#define HMAX 10007 // μ†Œμˆ˜κ°€ μ’‹λ‹€.
struct Node {
int idx;
T val;
};
int nidx;
struct Hash {
vector<Node> data[HMAX];
Hash() { init(); }
void init() {
for(int i = 0; i < HMAX; ++i) data[i].clear();
}
int getKey(int idx) {
return idx % HMAX;
}
void set(int idx, T val) {
if (getIdx(idx) == -1) insert(idx, val);
else data[getKey(idx)][getIdx(idx)] = {idx, val};
}
void insert(int idx, T val) { // 쀑볡 key값이 ν™•μ‹€νžˆ μ—†μŒμ„ μ•Œ λ•Œ
data[getKey(idx)].push_back({idx, val});
}
int getIdx(int idx) {
int i = 0;
for(auto& item: data[getKey(idx)]) {
if (item.idx == idx) return i;
++i;
}
return -1; // not found
}
};

Time Complexity

  • Insert: O(1)
  • Erase: O(1)
  • Find: O(1)
🏷️ 주제 λͺ©λ‘: